Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / ble / src / androidMain / kotlin / org / meshtastic / core / ble / AndroidBluetoothRepository.kt

Displaying Raw • Download

core/ble/src/androidMain/kotlin/org/meshtastic/core/ble/AndroidBluetoothRepository.kt 01cd54907d62cd90913d3d071b6bc240cae365ef (01cd5490) Text, 15.74 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.ble

Tff7b72import T7ee787android.Manifest
Tff7b72import T7ee787android.annotation.SuppressLint
Tff7b72import T7ee787android.bluetooth.BluetoothAdapter
Tff7b72import T7ee787android.bluetooth.BluetoothManager
Tff7b72import T7ee787android.content.Context
Tff7b72import T7ee787android.content.pm.PackageManager
Tff7b72import T7ee787android.os.Build
Tff7b72import T7ee787androidx.core.content.ContextCompat
Tff7b72import T7ee787androidx.lifecycle.Lifecycle
Tff7b72import T7ee787androidx.lifecycle.coroutineScope
Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CompletableDeferred
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.StateFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.asStateFlow
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787kotlinx.coroutines.withTimeoutOrNull
Tff7b72import T7ee787org.koin.core.annotation.Named
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.model.util.anonymize
Tff7b72import T7ee787kotlin.time.Duration
Tff7b72import T7ee787kotlin.time.Duration.Companion.milliseconds
Tff7b72import T7ee787kotlin.time.Duration.Companion.seconds

Tff7b72private Tff7b72val Te6edf3BOND_TIMEOUT Tff7b72= T79c0ff3T79c0ff0.Te6edf3seconds
Tff7b72private Tff7b72val Te6edf3BOND_STATE_POLL_INTERVAL Tff7b72= T79c0ff5T79c0ff0T79c0ff0.Te6edf3milliseconds

T8b949e// Fixed two-poll grace for slow BOND_NONE -> BOND_BONDING transitions after createBond() returns true.
T8b949e// Tune this or track observed transition latency if a specific OEM needs a longer window.
Tff7b72private Tff7b72val Te6edf3CREATED_BOND_NONE_GRACE Tff7b72= Te6edf3BOND_STATE_POLL_INTERVAL Tff7b72+ Te6edf3BOND_STATE_POLL_INTERVAL
Tff7b72internal Tff7b72const Tff7b72val Te6edf3BOND_FAILED_OR_REJECTED_MESSAGE Tff7b72= Ta5d6ff"Ta5d6ffBonding failed or rejectedTa5d6ff"

T8b949e/** Android implementation of [BluetoothRepository]. */
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooManyFunctionsTa5d6ff"Tb4b4b4)
Tf0883e@Single
Tff7b72class T56d364AndroidBluetoothRepositoryTb4b4b4(
Tff7b72private Tff7b72val Te6edf3contextTb4b4b4: Te6edf3ContextTb4b4b4,
Tff7b72private Tff7b72val Te6edf3dispatchersTb4b4b4: Te6edf3CoroutineDispatchersTb4b4b4,
Tf0883e@NamedTb4b4b4(Ta5d6ff"Ta5d6ffProcessLifecycleTa5d6ff"Tb4b4b4) Tff7b72private Tff7b72val Te6edf3processLifecycleTb4b4b4: Te6edf3LifecycleTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3BluetoothRepository Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3bluetoothManager Tff7b72= Te6edf3contextTb4b4b4.Te6edf3getSystemServiceTb4b4b4(Te6edf3ContextTb4b4b4.Te6edf3BLUETOOTH_SERVICETb4b4b4) Tff7b72as Te6edf3BluetoothManager
Tff7b72private Tff7b72val Te6edf3bluetoothAdapterTb4b4b4: Te6edf3BluetoothAdapter? Tff7b72= Te6edf3bluetoothManagerTb4b4b4.Te6edf3adapter

Tff7b72private Tff7b72val Te6edf3_state Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Te6edf3BluetoothStateTb4b4b4(Te6edf3hasPermissions Tff7b72= Te6edf3hasBluetoothPermissionsTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72override Tff7b72val Te6edf3stateTb4b4b4: Te6edf3StateFlowTff7b72<Te6edf3BluetoothStateTff7b72> Tff7b72= Te6edf3_stateTb4b4b4.Te6edf3asStateFlowTb4b4b4(Tb4b4b4)

Tff7b72private Tff7b72val Te6edf3deviceCache Tff7b72= Te6edf3mutableMapOfTff7b72<Tffa657StringTb4b4b4, Te6edf3MeshtasticBleDeviceTff7b72>Tb4b4b4(Tb4b4b4)

Tff7b72init Tb4b4b4{
Te6edf3processLifecycleTb4b4b4.Te6edf3coroutineScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3dispatchersTb4b4b4.Te6edf3defaultTb4b4b4) Tb4b4b4{ Te6edf3updateBluetoothStateTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffhasBluetoothPermissionsTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72if Tb4b4b4(Te6edf3BuildTb4b4b4.Te6edf3VERSIONTb4b4b4.Te6edf3SDK_INT Tff7b72>Tff7b72= Te6edf3BuildTb4b4b4.Te6edf3VERSION_CODESTb4b4b4.Te6edf3STb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3hasConnect Tff7b72=
Te6edf3ContextCompatTb4b4b4.Te6edf3checkSelfPermissionTb4b4b4(Te6edf3contextTb4b4b4, Te6edf3ManifestTb4b4b4.Te6edf3permissionTb4b4b4.Te6edf3BLUETOOTH_CONNECTTb4b4b4) Tff7b72=Tff7b72=
Te6edf3PackageManagerTb4b4b4.Te6edf3PERMISSION_GRANTED
Tff7b72val Te6edf3hasScan Tff7b72=
Te6edf3ContextCompatTb4b4b4.Te6edf3checkSelfPermissionTb4b4b4(Te6edf3contextTb4b4b4, Te6edf3ManifestTb4b4b4.Te6edf3permissionTb4b4b4.Te6edf3BLUETOOTH_SCANTb4b4b4) Tff7b72=Tff7b72=
Te6edf3PackageManagerTb4b4b4.Te6edf3PERMISSION_GRANTED
Te6edf3hasConnect Tff7b72&Tff7b72& Te6edf3hasScan
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Pre-Android 12: classic Bluetooth permissions are install-time.
Tff7b72true
Tb4b4b4}

Tff7b72override Tff7b72fun Td2a8ffrefreshStateTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3processLifecycleTb4b4b4.Te6edf3coroutineScopeTb4b4b4.Te6edf3launchTb4b4b4(Te6edf3dispatchersTb4b4b4.Te6edf3defaultTb4b4b4) Tb4b4b4{ Te6edf3updateBluetoothStateTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tff7b72override Tff7b72fun Td2a8ffisValidTb4b4b4(Te6edf3bleAddressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3BluetoothAdapterTb4b4b4.Te6edf3checkBluetoothAddressTb4b4b4(Te6edf3bleAddressTb4b4b4)

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionThrownTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4)
Tf0883e@SuppressLintTb4b4b4(Ta5d6ff"Ta5d6ffMissingPermissionTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffbondTb4b4b4(Te6edf3deviceTb4b4b4: Te6edf3BleDeviceTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3macAddress Tff7b72= Te6edf3deviceTb4b4b4.Te6edf3address
Tff7b72val Te6edf3remoteDevice Tff7b72=
Te6edf3bluetoothAdapterTff7b72?.Te6edf3getRemoteDeviceTb4b4b4(Te6edf3macAddressTb4b4b4) Tff7b72?: Tff7b72throw Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffBluetooth adapter unavailableTa5d6ff"Tb4b4b4)

Tff7b72if Tb4b4b4(Te6edf3remoteDeviceTb4b4b4.Te6edf3bondState Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4) Tb4b4b4{
Te6edf3updateBluetoothStateTb4b4b4(Tb4b4b4)
Tff7b72return
Tb4b4b4}

Tff7b72try Tb4b4b4{
Tff7b72val Te6edf3bonded Tff7b72=
Te6edf3withTimeoutOrNullTb4b4b4(Te6edf3BOND_TIMEOUTTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3result Tff7b72= Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3receiver Tff7b72= Te6edf3createBondReceiverTb4b4b4(Te6edf3macAddressTb4b4b4, Te6edf3resultTb4b4b4)

Tff7b72val Te6edf3filter Tff7b72=
Te6edf3androidTb4b4b4.Te6edf3contentTb4b4b4.Te6edf3IntentFilterTb4b4b4(Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3ACTION_BOND_STATE_CHANGEDTb4b4b4)
Te6edf3ContextCompatTb4b4b4.Te6edf3registerReceiverTb4b4b4(Te6edf3contextTb4b4b4, Te6edf3receiverTb4b4b4, Te6edf3filterTb4b4b4, Te6edf3ContextCompatTb4b4b4.Te6edf3RECEIVER_NOT_EXPORTEDTb4b4b4)

Tff7b72try Tb4b4b4{
Tff7b72val Te6edf3start Tff7b72= Te6edf3startOrObserveBondTb4b4b4(Te6edf3remoteDeviceTb4b4b4, Te6edf3resultTb4b4b4)
Te6edf3awaitBondResultTb4b4b4(Te6edf3remoteDeviceTb4b4b4, Te6edf3resultTb4b4b4, Te6edf3startTb4b4b4)
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3unregisterBondReceiverTb4b4b4(Te6edf3receiverTb4b4b4)
Tb4b4b4}
T8b949e// Reaching here means the suspended bond wait completed before BOND_TIMEOUT.
Tff7b72true
Tb4b4b4} Tff7b72?: Tb4b4b4(Te6edf3remoteDeviceTb4b4b4.Te6edf3bondState Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4)

Tff7b72if Tb4b4b4(Tff7b72!Te6edf3bondedTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffTimed out waiting for bonding to completeTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3updateBluetoothStateTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4)
Tf0883e@SuppressLintTb4b4b4(Ta5d6ff"Ta5d6ffMissingPermissionTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffremoveBondTb4b4b4(Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tb4b4b4{
Tff7b72val Te6edf3remoteDevice Tff7b72= Te6edf3bluetoothAdapterTff7b72?.Te6edf3getRemoteDeviceTb4b4b4(Te6edf3addressTb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3remoteDevice Tff7b72=Tff7b72= Tff7b72null Tff7b72|Tff7b72| Te6edf3remoteDeviceTb4b4b4.Te6edf3bondState Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONETb4b4b4) Tb4b4b4{
Tff7b72return Tff7b72false
Tb4b4b4}
Tff7b72return Tff7b72try Tb4b4b4{
T8b949e// removeBond() is a public-but-hidden BluetoothDevice API (no SDK stub); reflection is the standard access
T8b949e// path used across the Android BLE/DFU ecosystem (incl. Nordic's DFU library).
Tff7b72val Te6edf3removed Tff7b72= Te6edf3remoteDeviceTb4b4b4.Te6edf3javaClassTb4b4b4.Te6edf3getMethodTb4b4b4(Ta5d6ff"Ta5d6ffremoveBondTa5d6ff"Tb4b4b4)Tb4b4b4.Te6edf3invokeTb4b4b4(Te6edf3remoteDeviceTb4b4b4) Tff7b72as? Tffa657Boolean Tff7b72?: Tff7b72false
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffremoveBond(Tffd700${Te6edf3addressTb4b4b4.Te6edf3anonymizeTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff) -> Tffd700$Te6edf3removedTa5d6ff" Tb4b4b4}
Te6edf3removed
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffremoveBond(Tffd700${Te6edf3addressTb4b4b4.Te6edf3anonymizeTb4b4b4(Tb4b4b4)Tffd700}Ta5d6ff) reflection failedTa5d6ff" Tb4b4b4}
Tff7b72false
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3updateBluetoothStateTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4)
Tf0883e@SuppressLintTb4b4b4(Ta5d6ff"Ta5d6ffMissingPermissionTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffstartOrObserveBondTb4b4b4(
Te6edf3remoteDeviceTb4b4b4: Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4,
Te6edf3resultTb4b4b4: Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3BondWaitStart Tb4b4b4{
Tff7b72var Te6edf3start Tff7b72= Te6edf3BondWaitStartTb4b4b4(Tb4b4b4)
Tff7b72try Tb4b4b4{
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3resultTb4b4b4.Te6edf3isCompletedTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3remoteDeviceTb4b4b4.Te6edf3bondState Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4) Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(Te6edf3remoteDeviceTb4b4b4.Te6edf3createBondTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3start Tff7b72= Te6edf3BondWaitStartTb4b4b4(Te6edf3createdBond Tff7b72= Tff7b72trueTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// createBond() returns false when a bond is already in flight, triggered by a GATT
T8b949e// operation hitting a secured characteristic, or already established.
T8b949e// ACTION_BOND_STATE_CHANGED is unreliable on some devices (see Kable #111), so
T8b949e// re-check bondState directly rather than failing the whole flow.
Tff7b72when Tb4b4b4(Te6edf3remoteDeviceTb4b4b4.Te6edf3bondStateTb4b4b4) Tb4b4b4{
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDED Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4}

Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDING Tff7b72-Tff7b72> Tb4b4b4{
T8b949e// Bond already in progress; leave the receiver registered to resolve it on
T8b949e// the terminal BOND_BONDED / BOND_NONE transition instead of treating this
T8b949e// as a failure.
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffcreateBond() returned false but bonding is already in progressTa5d6ff" Tb4b4b4}
Te6edf3start Tff7b72= Te6edf3BondWaitStartTb4b4b4(Te6edf3bondingObserved Tff7b72= Tff7b72trueTb4b4b4)
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeExceptionallyTb4b4b4(Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffFailed to initiate bondingTa5d6ff"Tb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeExceptionallyTb4b4b4(Te6edf3eTb4b4b4)
Tb4b4b4}
Tff7b72return Te6edf3start
Tb4b4b4}

Tff7b72private Tff7b72data Tff7b72class T56d364BondWaitStartTb4b4b4(Tff7b72val Te6edf3bondingObservedTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72falseTb4b4b4, Tff7b72val Te6edf3createdBondTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72falseTb4b4b4)

Tf0883e@SuppressLintTb4b4b4(Ta5d6ff"Ta5d6ffMissingPermissionTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffawaitBondResultTb4b4b4(
Te6edf3remoteDeviceTb4b4b4: Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4,
Te6edf3resultTb4b4b4: Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4,
Te6edf3startTb4b4b4: Te6edf3BondWaitStartTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72var Te6edf3bondingWasInFlight Tff7b72= Te6edf3startTb4b4b4.Te6edf3bondingObserved
T8b949e// createBond() can return true before Android reports BOND_BONDING. Tolerate two polled
T8b949e// BOND_NONE samples (polls 1-2), then fail on the third persistent BOND_NONE.
Tff7b72var Te6edf3createdBondNoneGraceRemaining Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3startTb4b4b4.Te6edf3createdBondTb4b4b4) Tb4b4b4{
Te6edf3CREATED_BOND_NONE_GRACE
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3DurationTb4b4b4.Te6edf3ZERO
Tb4b4b4}

Tff7b72while Tb4b4b4(Tff7b72!Te6edf3resultTb4b4b4.Te6edf3isCompletedTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3completedFromReceiver Tff7b72=
Te6edf3withTimeoutOrNullTb4b4b4(Te6edf3BOND_STATE_POLL_INTERVALTb4b4b4) Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tff7b72true
Tb4b4b4} Tff7b72=Tff7b72= Tff7b72true

Tff7b72if Tb4b4b4(Tff7b72!Te6edf3completedFromReceiverTb4b4b4) Tb4b4b4{
Tff7b72when Tb4b4b4(Te6edf3remoteDeviceTb4b4b4.Te6edf3bondStateTb4b4b4) Tb4b4b4{
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDED Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4}

Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDING Tff7b72-Tff7b72> Tb4b4b4{
T8b949e// Once polling observes BOND_BONDING, a later BOND_NONE is terminal. Keep this
T8b949e// defensive for any path that observes in-flight bonding outside the start state.
Te6edf3bondingWasInFlight Tff7b72= Tff7b72true
Te6edf3createdBondNoneGraceRemaining Tff7b72= Te6edf3DurationTb4b4b4.Te6edf3ZERO
Tb4b4b4}

Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONE Tff7b72-Tff7b72> Tb4b4b4{
T8b949e// Invariant: if start.createdBond is false, startOrObserveBond either completed
T8b949e// result or observed BOND_BONDING, which is represented by bondingWasInFlight.
Tff7b72val Te6edf3pollFailureDetails Tff7b72=
Ta5d6ff"Ta5d6ffbond poll failed: bondingWasInFlight=Tffd700$Te6edf3bondingWasInFlightTa5d6ff Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffcreatedBond=Tffd700${Te6edf3startTb4b4b4.Te6edf3createdBondTffd700}Ta5d6ff graceRemaining=Tffd700$Te6edf3createdBondNoneGraceRemainingTa5d6ff"
Tff7b72when Tb4b4b4{
Te6edf3bondingWasInFlight Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Te6edf3pollFailureDetails Tb4b4b4}
Te6edf3resultTb4b4b4.Te6edf3completeExceptionallyTb4b4b4(Te6edf3ExceptionTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4)Tb4b4b4)
Tb4b4b4}

Te6edf3createdBondNoneGraceRemaining Tff7b72> Te6edf3DurationTb4b4b4.Te6edf3ZERO Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3createdBondNoneGraceRemaining Tff7b72-Tff7b72= Te6edf3BOND_STATE_POLL_INTERVAL
Tb4b4b4}

Te6edf3startTb4b4b4.Te6edf3createdBond Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Te6edf3pollFailureDetails Tb4b4b4}
Te6edf3resultTb4b4b4.Te6edf3completeExceptionallyTb4b4b4(Te6edf3ExceptionTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Te6edf3resultTb4b4b4.Te6edf3awaitTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionThrownTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffcreateBondReceiverTb4b4b4(
Te6edf3macAddressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3resultTb4b4b4: Te6edf3CompletableDeferredTff7b72<Tffa657UnitTff7b72>Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3androidTb4b4b4.Te6edf3contentTb4b4b4.Te6edf3BroadcastReceiver Tff7b72= Tff7b72object Tb4b4b4: T56d364androidTb4b4b4.Te6edf3contentTb4b4b4.Te6edf3BroadcastReceiverTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72override Tff7b72fun Td2a8ffonReceiveTb4b4b4(Te6edf3cTb4b4b4: Te6edf3ContextTb4b4b4, Te6edf3intentTb4b4b4: Te6edf3androidTb4b4b4.Te6edf3contentTb4b4b4.Te6edf3IntentTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3intentTb4b4b4.Te6edf3action Tff7b72!Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3ACTION_BOND_STATE_CHANGEDTb4b4b4) Tff7b72return
Tff7b72val Te6edf3d Tff7b72=
Te6edf3androidxTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3contentTb4b4b4.Te6edf3IntentCompatTb4b4b4.Te6edf3getParcelableExtraTb4b4b4(
Te6edf3intentTb4b4b4,
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3EXTRA_DEVICETb4b4b4,
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTff7b72::Te6edf3classTb4b4b4.Te6edf3javaTb4b4b4,
Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3dTff7b72?.Te6edf3addressTff7b72?.Te6edf3equalsTb4b4b4(Te6edf3macAddressTb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4) Tff7b72!Tff7b72= Tff7b72trueTb4b4b4) Tff7b72return

Tff7b72val Te6edf3state Tff7b72=
Te6edf3intentTb4b4b4.Te6edf3getIntExtraTb4b4b4(
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3EXTRA_BOND_STATETb4b4b4,
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3ERRORTb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3prevState Tff7b72=
Te6edf3intentTb4b4b4.Te6edf3getIntExtraTb4b4b4(
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3EXTRA_PREVIOUS_BOND_STATETb4b4b4,
Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3ERRORTb4b4b4,
Tb4b4b4)

Tff7b72if Tb4b4b4(Te6edf3state Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDEDTb4b4b4) Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeTb4b4b4(Tffa657UnitTb4b4b4)
Tb4b4b4} Tff7b72else Tff7b72if Tb4b4b4(
Te6edf3state Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_NONE Tff7b72&Tff7b72&
Te6edf3prevState Tff7b72=Tff7b72= Te6edf3androidTb4b4b4.Te6edf3bluetoothTb4b4b4.Te6edf3BluetoothDeviceTb4b4b4.Te6edf3BOND_BONDING
Tb4b4b4) Tb4b4b4{
Te6edf3resultTb4b4b4.Te6edf3completeExceptionallyTb4b4b4(Te6edf3ExceptionTb4b4b4(Te6edf3BOND_FAILED_OR_REJECTED_MESSAGETb4b4b4)Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffSwallowedExceptionTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffunregisterBondReceiverTb4b4b4(Te6edf3receiverTb4b4b4: Te6edf3androidTb4b4b4.Te6edf3contentTb4b4b4.Te6edf3BroadcastReceiverTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3contextTb4b4b4.Te6edf3unregisterReceiverTb4b4b4(Te6edf3receiverTb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3ignoredTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{Tb4b4b4}
Tb4b4b4}

Tff7b72internal Tff7b72suspend Tff7b72fun Td2a8ffupdateBluetoothStateTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3enabled Tff7b72= Te6edf3bluetoothAdapterTff7b72?.Te6edf3isEnabled Tff7b72=Tff7b72= Tff7b72true
Tff7b72var Te6edf3hasPermissions Tff7b72= Te6edf3hasBluetoothPermissionsTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bondedDevices Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3hasPermissionsTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3getBondedAppPeripheralsTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3SecurityExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffSecurityException accessing bonded devices. Missing BLUETOOTH_CONNECT?Ta5d6ff" Tb4b4b4}
Te6edf3hasPermissions Tff7b72= Tff7b72false
Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3newState Tff7b72= Te6edf3BluetoothStateTb4b4b4(Te6edf3hasPermissions Tff7b72= Te6edf3hasPermissionsTb4b4b4, Te6edf3enabled Tff7b72= Te6edf3enabledTb4b4b4, Te6edf3bondedDevices Tff7b72= Te6edf3bondedDevicesTb4b4b4)

Te6edf3_stateTb4b4b4.Te6edf3emitTb4b4b4(Te6edf3newStateTb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3d Tb4b4b4{ Ta5d6ff"Ta5d6ffDetected our bluetooth access=Tffd700$Te6edf3newStateTa5d6ff" Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressLintTb4b4b4(Ta5d6ff"Ta5d6ffMissingPermissionTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72fun Td2a8ffgetBondedAppPeripheralsTb4b4b4(Tb4b4b4)Tb4b4b4: Te6edf3ListTff7b72<Te6edf3BleDeviceTff7b72> Tb4b4b4{
Tff7b72val Te6edf3bonded Tff7b72= Te6edf3bluetoothAdapterTff7b72?.Te6edf3bondedDevices Tff7b72?: Tff7b72return Te6edf3emptyListTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bondedAddresses Tff7b72= Te6edf3bondedTb4b4b4.Te6edf3mapToTb4b4b4(Te6edf3mutableSetOfTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3address Tb4b4b4}
T8b949e// Evict entries for devices that are no longer bonded and update names in case the
T8b949e// user renamed the device in firmware since the cache was populated.
Te6edf3deviceCacheTb4b4b4.Te6edf3keysTb4b4b4.Te6edf3retainAllTb4b4b4(Te6edf3bondedAddressesTb4b4b4)
Tff7b72return Te6edf3bondedTb4b4b4.Te6edf3map Tb4b4b4{ Te6edf3device Tff7b72-Tff7b72>
Tff7b72val Te6edf3cached Tff7b72= Te6edf3deviceCacheTb4b4b4.Te6edf3getOrPutTb4b4b4(Te6edf3deviceTb4b4b4.Te6edf3addressTb4b4b4) Tb4b4b4{ Te6edf3MeshtasticBleDeviceTb4b4b4(Te6edf3deviceTb4b4b4.Te6edf3addressTb4b4b4, Te6edf3deviceTb4b4b4.Te6edf3nameTb4b4b4) Tb4b4b4}
T8b949e// If the name changed (firmware rename, etc.), replace the cached entry and return the new one.
Tff7b72if Tb4b4b4(Te6edf3cachedTb4b4b4.Te6edf3name Tff7b72!Tff7b72= Te6edf3deviceTb4b4b4.Te6edf3nameTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3updated Tff7b72= Te6edf3MeshtasticBleDeviceTb4b4b4(Te6edf3deviceTb4b4b4.Te6edf3addressTb4b4b4, Te6edf3deviceTb4b4b4.Te6edf3nameTb4b4b4)
Te6edf3deviceCacheTff7b72[Te6edf3deviceTb4b4b4.Te6edf3addressTff7b72] Tff7b72= Te6edf3updated
Te6edf3updated
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3cached
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tf0883e@SuppressLintTb4b4b4(Ta5d6ff"Ta5d6ffMissingPermissionTa5d6ff"Tb4b4b4)
Tff7b72override Tff7b72fun Td2a8ffisBondedTb4b4b4(Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72try Tb4b4b4{
Te6edf3bluetoothAdapterTff7b72?.Te6edf3bondedDevicesTff7b72?.Te6edf3any Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3addressTb4b4b4.Te6edf3equalsTb4b4b4(Te6edf3addressTb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4) Tb4b4b4} Tff7b72?: Tff7b72false
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3SecurityExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffSecurityException checking bonded devices. Missing BLUETOOTH_CONNECT?Ta5d6ff" Tb4b4b4}
Tff7b72false
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.09s